-- Coding Format : UTF-8
-- 「agent.lua 脚本介绍」
-- 玩家需要服务，agent就是服务员，一个玩家对应一个agent
-- agent负责处理自己的玩家发来的消息，可以转发或广播消息，也可以做逻辑处理
local skynet = require "skynet"
local socket = require "skynet.socket"
local sprotoloader = require "sprotoloader"
require "User"
require "util"
require "ChatRecord"

local host
local proto_pack
local client_fd

local CMD = {}
local REQUEST = {}

local userManager
local chatService
local watchdog

-- 本agent服务的用户信息
local user

local function send_request(pack, fd)
    local package = string.pack(">s2", pack)
    socket.write(fd, package)
end

local function reply(protoName, res)
    send_request(proto_pack(protoName, res), client_fd)
end

local function getChatRecordByUsers(userA, userB)
    print("查询玩家间信息:" .. userA.username .. "和" .. userB.username)
    local records = skynet.call(chatService, "lua", "getRecords", userA:simple(), userB:simple())
    local speakers = {}
    local contents = {}
    for i, record in ipairs(records) do
        speakers[i] = record.sender
        contents[i] = record.content
        print(record.sender .. ":" .. record.content)
    end

    return speakers, contents
end

local function getUsers()
    -- 向userManager请求用户列表、在线用户列表
    local users = skynet.call(userManager, "lua", "getUsers")
    local usersOnline = skynet.call(userManager, "lua", "getUsersOnline")

    -- 遍历usersOnline，把users中在线的玩家删去
    for name, user in pairs(usersOnline) do
        users[name] = nil
    end

    -- 设置返回内容
    local res = {}
    res.code = 0
    res.message = "获取所有用户"
    local onlineNames = {}
    local offlineNames = {}
    for name, _ in pairs(usersOnline) do
        onlineNames[#onlineNames + 1] = name
    end
    for name, _ in pairs(users) do
        offlineNames[#offlineNames + 1] = name
    end
    res.accountOnline = onlineNames
    res.accountOffline = offlineNames
    return res
end

local function getFds()
    local res = skynet.call(watchdog, "lua", "getFds")
    -- local res = skynet.call(userManager, "lua", "getFds")
    return res
end

-- 响应proto中的c2s网络命令
-- 当收到包后，会根据包的proto c2s格式名，从REQUEST中找到对应的响应方法来执行
function REQUEST:req_test()
    print("recv client test req, " .. "data=" .. self.data)
    send_request(proto_pack("test_reply", {
        reply_data = {"reply--", self.data}
    }), client_fd)
end

function REQUEST:req_register()
    print("recv client register req, " .. "account=" .. self.account)
    local res = skynet.call(userManager, "lua", "addUser", {
        username = self.account,
        password = self.password
    })
    print(table_to_str(res))
    local code = res[1]
    local msg = res[2]
    send_request(proto_pack("register_reply", {
        reply_data = {code .. msg}
    }), client_fd)
end

function REQUEST:req_login()
    print("recv client login req, " .. "account=" .. self.account)
    local res = skynet.call(userManager, "lua", "login", {
        username = self.account,
        password = self.password,
        fd = client_fd
    })
    print(table_to_str(res))
    local code = res[1]
    local msg = res[2]
    local u = res[3]
    -- npc
    local npc = res[4]
    user = u
    
    for k, v in pairs(npc) do
        print(k)
        print(v)
    end
    -- 放入信息
    send_request(proto_pack("login_reply", {
        reply_data = {code .. msg},
        id = npc.Id,
        HP = npc.HP,
        ATK = npc.ATK,
        DEF = npc.DEF,
        Speed = npc.Speed
    }), client_fd)
    if code == 0 then
        -- 登录成功，则向所有玩家广播用户列表
        local userList = getUsers()
        -- 获取在线玩家的fd
        local fds = getFds()
        print("广播的用户数量:" .. #fds)
        for i, fd in ipairs(fds) do
            send_request(proto_pack("get_user", userList), fd)
        end
    end
end

-- req_get_user:获取所有用户，区分在线、不在线
function REQUEST:req_get_user()
    local res = getUsers()
    print(table_to_str(res))
    reply("get_user", res)
end

-- req_append_chat_record:发送信息
function REQUEST:req_append_chat_record()
    local code;
    local message;
    local sender = self.sender
    local receiver = self.receiver
    local record = self.record
    -- 根据名字查找用户
    local userS = skynet.call(userManager, "lua", "getUserByName", sender)
    local userR = skynet.call(userManager, "lua", "getUserByName", receiver)
    if userS == nil or userR == nil then
        -- 找不到则失败
        code = 1;
        message = "用户不存在"
    else
        userS = User:newByString(userS):simple()
        userR = User:newByString(userR):simple()
        -- 找得到，请求存入消息
        skynet.call(chatService, "lua", "addRecord", userS, userR, record)
        code = 1
        message = "发送成功"
        -- 也向另一个玩家发送,根据名字查找fd
        local fdR = skynet.call(userManager, "lua", "getFdByName", userR.username)
        print("fd receiver:" .. fdR)
        if fdR ~= nil then
            local speakers, contents = getChatRecordByUsers(userS, userR)
            send_request(proto_pack("get_chat_record", {
                code = 0,
                message = "新消息更新",
                speakers = speakers,
                contents = contents
            }), fdR)
        end
    end
    local res = {
        code = code,
        message = message
    }
    reply("append_chat_record", res)
end

-- req_get_chat_record:查询两个玩家之间的所有消息
function REQUEST:req_get_chat_record()
    local accountA = self.accountA
    local accountB = self.accountB
    local code
    local message
    -- 根据名字查找用户
    local userA = skynet.call(userManager, "lua", "getUserByName", accountA)
    local userB = skynet.call(userManager, "lua", "getUserByName", accountB)
    -- 查询消息记录
    if userA == nil or userB == nil then
        -- 找不到则失败
        code = 1;
        message = "用户不存在"
    else
        userA = User:newByString(userA)
        userB = User:newByString(userB)
        local speakers, contents = getChatRecordByUsers(userA, userB)
        local res = {
            code = code,
            message = message,
            speakers = speakers,
            contents = contents
        }
        reply("get_chat_record", res)
    end
end

local function request(name, args, response)
    local f = assert(REQUEST[name])
    local r = f(args)
end

skynet.register_protocol {
    name = "client",
    id = skynet.PTYPE_CLIENT,
    unpack = function(msg, sz)
        return host:dispatch(msg, sz)
    end,
    dispatch = function(fd, _, type, ...)
        skynet.ignoreret() -- session is fd, don't call skynet.ret
        if type == "REQUEST" then
            args = ...

            local ok, result = pcall(request, ...)
            if ok then
                if result then
                    send_request(result, fd)
                end
            else
                skynet.error(result)
            end
        else
            assert(type == "RESPONSE")
            error "This example doesn't support request client"
        end
    end
}

function CMD.start(conf)
    local fd = conf.client
    local gate = conf.gate
    client_fd = fd

    -- slot 1,2 set at main.lua
    host = sprotoloader.load(1):host "package"
    proto_pack = host:attach(sprotoloader.load(2))
    skynet.call(gate, "lua", "forward", fd)
    print("fd is ------: ", fd)

    -- 发送连接成功包给客户端
    send_request(proto_pack("connect_ok"), client_fd)

end

function CMD.disconnect(fd)
    -- 关闭时登出
    skynet.call(userManager, "lua", "logout", user)
    skynet.exit()
end

skynet.start(function()
    skynet.dispatch("lua", function(_, _, command, ...)
        local f = CMD[command]
        userManager = skynet.queryservice("userManager")
        chatService = skynet.queryservice("chatService")
        watchdog = skynet.queryservice("watchdog")
        skynet.ret(skynet.pack(f(...)))
    end)
end)
